﻿
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Collections;
using System.Threading;

namespace sendfile_server
{
    public abstract class SFConnection
    {
        protected TcpClient m_TcpClient;
        protected bool m_IsConnected = false;
        protected IPEndPoint m_ClientIP = null;


        // inner thread
        protected ReadThread m_ReadThread;
        protected IdleThread m_IdleThread;

        // for idle ping thread 
        protected int m_IdleWorkerInterval = 6;
        protected int m_PingInterval = 6;
        protected int m_PingTimeout = 30;
        protected DateTime m_LastPingSent;
        protected DateTime m_LastPongReceived;
        protected TimeSpan m_Lag;

        //---------------------------------------------------------------------
        // Events
        //---------------------------------------------------------------------
        public event EventHandler OnDisconnecting;
        public event EventHandler OnDisconnected;
        public event EventHandler OnConnectionError;

        //---------------------------------------------------------------------
        // property
        //---------------------------------------------------------------------
        public bool IsConnected
        {
            get { return m_IsConnected; }
            set { m_IsConnected = value; }
        }

        public IPEndPoint ClientIP
        {
            get { return m_ClientIP; }
            set { m_ClientIP = value; }
        }

        public int IdleWorkerInterval
        {
            get { return m_IdleWorkerInterval; }
            set { m_IdleWorkerInterval = value; }
        }

        public int PingInterval
        {
            get { return m_PingInterval; }
            set { m_PingInterval = value; }
        }

        public int PingTimeout
        {
            get { return m_PingTimeout; }
            set { m_PingTimeout = value; }
        }

        public DateTime LastPingSent
        {
            get { return m_LastPingSent; }
            set { m_LastPingSent = value; }
        }

        public DateTime LastPongReceived
        {
            get { return m_LastPongReceived; }
            set { m_LastPongReceived = value; }
        }

        public TimeSpan Lag
        {
            get { return m_Lag; }
            set { m_Lag = value; }
        }



        /// <summary>
        /// normally construct in client
        /// </summary>
        public SFConnection()
        {
        }

        public virtual void Disconnect()
        {
            if (m_TcpClient.Connected)
            {
                m_TcpClient.Close();
                if (OnDisconnected != null)
                {
                    OnDisconnected(this, EventArgs.Empty);
                }
            }

            if (m_ReadThread != null)
                m_ReadThread.Stop();
            if (m_IdleThread != null)
                m_IdleThread.Stop();
            Console.WriteLine("[{0,3}] - {1} : Disconnect",
                Thread.CurrentThread.ManagedThreadId,
                DateTime.Now.ToString("HH:mm:ss.ffff"));
        }



        public NetworkStream GetStream()
        {
            if (m_TcpClient != null)
            {
                return m_TcpClient.GetStream();
            }
            return null;
        }

        public int SendMsg(SFMessage msg)
        {
            try
            {
                lock(m_TcpClient.GetStream())
                {
                    NetworkStream ns = GetStream();
                    ns.Write(msg.GetBytes(), 0, msg.GetBytes().Length);
                    ns.Flush();
                    // TraceMsg("Sending " + Enum.GetName(typeof(MessageTypeEnum), msg.MessageType) + " msg");
                    return 1;
                }
                
            }
            catch(Exception ex)
            {
                throw ex;
            }
            
        }

        public void TraceMsg(string msg)
        {
            Console.WriteLine("[{0,3}] - {1} : {2}",
                    Thread.CurrentThread.ManagedThreadId,
                    DateTime.Now.ToString("HH:mm:ss.ffff"), msg);
        }

    }   // end class 

    class SFConnectionUnitTest
    {
        /// <summary>
        /// test client sent msg to server, server process message queue
        /// </summary>
        public static void TestConnection()
        {
            StartServerThread();

            StartClientThread();
        }

        private static void TraceMsg(string msg)
        {
            Console.WriteLine("[{0,3}] - {1} : {2}",
                    Thread.CurrentThread.ManagedThreadId,
                    DateTime.Now.ToString("HH:mm:ss.ffff"), msg);
        }

        /// <summary>
        /// start server thread, for every connection, start read thread to read message
        /// </summary>
        private static void StartServerThread()
        {
            Thread serverThread = new Thread(delegate()
            {
                TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1080);
                listener.ExclusiveAddressUse = false;
                listener.Start();
                TraceMsg("Listening on port 1080");

                while (true)
                {
                    TcpClient clientB = listener.AcceptTcpClient();
                    SFServerConnection con = new SFServerConnection(clientB);

                }
            });
            serverThread.Start();
        }

        /// <summary>
        /// start client thread to connect the server thread
        /// </summary>
        private static void StartClientThread()
        {
            Thread t1 = new Thread(delegate()
            {
                SFClientConnection con = new SFClientConnection();
                con.Connect("127.0.0.1", 1080);
                TraceMsg("Connection to server");


                NetworkStream ns = con.GetStream();

                // send basic message
                //for (int i = 0; i < 1; i++)
                //{
                //    byte[] buf = new byte[4] { 0x01, 0x02, 0x03, 0x04 };
                //    SFMessage smsg = new SFMessage((int)MessageTypeEnum.BASIC, buf);
                //    ns.Write(smsg.GetBytes(), 0, smsg.GetBytes().Length);
                //    TraceMsg("send basic message");
                //}

                // send FileDataMessage
                for (int i = 0; i < 1; i++)
                {
                    byte[] filedata = new byte[] { 0xff, 0xff, 0xff, 0xff };
                    SFMessage fmsg3 = new FileDataMessage("phone.txt", filedata);
                    ns.Write(fmsg3.GetBytes(), 0, fmsg3.GetBytes().Length);
                    TraceMsg("send filedata");
                }

            });
            t1.Start();

        }

    }

}